package haxeframe.physics;

import box2D.dynamics.B2World;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.xml.XMLNodeType;
import haxeframe.physics.Material;
import haxeframe.physics.Symbol;
import haxeframe.utils.ObjectPool;

import openfl.display.Tilesheet;

class Assets
{
	private static var _materials:Map<String, ObjectPool> = new Map<String, ObjectPool>();
	private static var _symbols:Map<String, ObjectPool> = new Map<String, ObjectPool>();

	private static var _tilesheets:Map<BitmapData, Tilesheet> = new Map<BitmapData, Tilesheet>();
	private static var _tiles:Map<String, Array<Dynamic>> = new Map<String, Array<Dynamic>>();
	
	public static function addTilesheet(sourceBitmapData:BitmapData, tilesheetXml:Xml):Void
	{
		if(sourceBitmapData == null || tilesheetXml == null) return;
		var tilesheet:Tilesheet = new Tilesheet(sourceBitmapData);
		var tileNum:Int = 0;

        for(element in tilesheetXml.elements())
        {
        	var rect:Rectangle = new Rectangle(
        		Math.round(Std.parseInt(element.get('x'))),
        		Math.round(Std.parseInt(element.get('y'))),
        		Math.round(Std.parseInt(element.get('width'))),
        		Math.round(Std.parseInt(element.get('height')))
        	);
        	tilesheet.addTileRect(rect);
            _tiles.set(element.get('name'), [tilesheet, -Math.round(Std.parseInt(element.get('frameX'))), -Math.round(Std.parseInt(element.get('frameY'))), tileNum, sourceBitmapData, rect]);
            ++tileNum;
        }
        _tilesheets.set(sourceBitmapData, tilesheet);
	}

	public static function getTilesheet(sourceBitmapData:BitmapData):Tilesheet
	{
		if(sourceBitmapData == null) return null;
		return _tilesheets.get(sourceBitmapData);
	}

	public static function drawTile(tileName:String, target:Sprite, centered:Bool=false):Void
	{
		var tile:Array<Dynamic> = _tiles.get(tileName);
		if(tile == null) return;

		target.graphics.clear();
        tile[0].drawTiles(target.graphics, [tile[1]*Globals.SCALE_FACTOR, tile[2]*Globals.SCALE_FACTOR, tile[3], Globals.SCALE_FACTOR], true, Tilesheet.TILE_SCALE);
            
	}

	public static function getTileSettings(tileName:String):Array<Dynamic>
	{
		return _tiles.get(tileName);
	}

	public static function getTileAsBitmap(tileName:String):Bitmap
	{
		var tile:Array<Dynamic> = _tiles.get(tileName);
		if(tile == null) return null;
		var bmd:BitmapData = new BitmapData(tile[5].width, tile[5].height);
		bmd.copyPixels(tile[4], tile[5], new Point());
		return new Bitmap(bmd);
	}

	public static function addMaterials(xml:Xml):Void
	{
		var materials = xml.elementsNamed('material');
		for(m in materials){
			addMaterial(m);
		}
	}
	
	public static function addMaterial(xml:Xml):Void
	{
		// Note, if this material already exists, it will be overwritten.
		_materials.set(xml.get('name'), new ObjectPool(Material, [xml]));
	}
	
	public static function getMaterial(material:String):Material
	{
		var op:ObjectPool = _materials.get(material);
		if(op != null)
			return cast(op.getObject(), Material);
		else
			return null;
	}
	
	public static function getMaterialLength(material:String):Int
	{
		var op:ObjectPool = _materials.get(material);
		return op.length;
	}
	
	public static function returnMaterial(material:String, object:Material):Void
	{
		var op:ObjectPool = _materials.get(material);
		if(op != null) op.returnObject(object);
	}
	
	public static function addSymbols(xml:Xml, world:B2World, scale:Float):Void
	{
		if(xml != null){
			for(s in xml.firstElement().elementsNamed('symbol')){
				addSymbol(s, world, scale);
			}
		}
	}
	
	public static function addSymbol(xml:Xml, world:B2World, scale:Float):Void
	{
		// Note, if this symbol already exists, it will be overwritten.
		var symbolClass:Class<Dynamic> = Symbol;
		if(xml.get('className') != null)
			if(Type.resolveClass(xml.get('className')) != null)
				symbolClass = Type.resolveClass(xml.get('className'));

		var op:ObjectPool = new ObjectPool(symbolClass, [xml, world, scale]);
		op.name = xml.get('name');
		_symbols.set(xml.get('name'), op);
	}
	
	public static function getSymbol(symbol:String):Dynamic
	{
		var op:ObjectPool = _symbols.get(symbol);
		if(op != null)
			return op.getObject();
		else
			return null;
	}
	
	public static function returnSymbol(symbol:String, object:Symbol):Void
	{
		var op:ObjectPool = _symbols.get(symbol);
		if(op != null)
			op.returnObject(object);
	}
	
	public static function getSymbolLength(symbol:String):Int
	{
		var op:ObjectPool = _symbols.get(symbol);
		return op.length;
	}
	
	public static function clearMaterials():Void
	{
		if(_materials != null){
			for(m in _materials){
				m.destroy();
			}
		}
		_materials = new Map<String, ObjectPool>();
	}
	
	public static function clearSymbols():Void
	{
		if(_symbols != null){
			for(s in _symbols){
				s.destroy();
			}
		}
		_symbols = new Map<String, ObjectPool>();
	}
	
	public static function clearTilesheets():Void
	{
		_tilesheets = new Map<BitmapData, Tilesheet>();
		_tiles = new Map<String, Array<Dynamic>>();
	}
	
	public static function clearAll():Void
	{
		clearMaterials();
		clearSymbols();
		clearTilesheets();
	}
}